#include <ctime>
#include <vector>
#include "wnds_mgmt.h"
#include "stream.h"
#include "process_mgmt.h"

using namespace std;


static DWORD WINAPI closeWindowsProc(LPVOID lpParam);
static FunctionSuccesFailure closeOneWindow(window_info &wndToClose);
static INT isClassWndLast(const window_info &wnd);
static FunctionSuccesFailure waitWndExpiration(HWND hwnd);
static BOOL &getWndProcUpdateBlockade();
static FunctionSuccesFailure fontDefaultSettings(LOGFONT &fontDefaultStruct);
static INT fontFactorInit(UINT_PTR hWindowRaw);

/******************************************************************************
*
*               window info struct members
*
******************************************************************************/
window_info::window_info
    (const string &cNameRaw, const string &tNameRaw, LRESULT (CALLBACK *wProc)(HWND, UINT, WPARAM, LPARAM), UINT sX, UINT sY, UINT wd, UINT ht)
{
    this->window_info::window_info();

    if(cNameRaw != UNNAMED) {
        wstring cNameWs = stringToWc(cNameRaw);
        LPCTSTR cName   = cNameWs.c_str();
        UINT cNameSize  = cNameWs.size()+SPACE_FOR_NULL;
        wClassName      = new WCHAR[cNameSize];

        if(wClassName != NULL)
            copyWCharString(wClassName, cNameSize, cName);
    }

    if(tNameRaw != UNNAMED) {
        wstring tNameWs = stringToWc(tNameRaw);
        LPCTSTR tName   = tNameWs.c_str();
        UINT tNameSize  = tNameWs.size()+SPACE_FOR_NULL;
        wTitleName      = new WCHAR[tNameSize];

        if(wTitleName != NULL)
            copyWCharString(wTitleName, tNameSize, tName);
    }

    WndProc = wProc; start_X = sX; start_Y = sY; width = wd; height = ht;
}

window_info::window_info(const window_info &toCopy)
{
    this->window_info::window_info(wcToString(toCopy.wClassName), wcToString(toCopy.wTitleName),
                             toCopy.WndProc, toCopy.start_X, toCopy.start_Y, toCopy.width, toCopy.height);
    hwnd = toCopy.hwnd;
}

window_info &window_info::operator=(const window_info &toAssign) {

    if(this != &toAssign) {
        this->window_info::~window_info();
        this->window_info::window_info(wcToString(toAssign.wClassName), wcToString(toAssign.wTitleName),
                                 toAssign.WndProc, toAssign.start_X, toAssign.start_Y, toAssign.width, toAssign.height);
        hwnd = toAssign.hwnd;
    }

    return *this;
}

window_info::window_info(HWND wndHwnd)
{
    static const UINT bufferSizeMax = 255+SPACE_FOR_NULL;
    WCHAR buffer[bufferSizeMax] = {0};
    UINT  wcTextSize            = 0;

    this->window_info::window_info();
    hwnd = wndHwnd;

    GetClassName(wndHwnd, buffer, bufferSizeMax);
    wcTextSize        = wcslen(buffer)+SPACE_FOR_NULL;
    wClassName        = new WCHAR[wcTextSize];
    //OUTSTREAM<<endl<<"cName: "<<wcToString(buffer)<<" ptr_cName: "<<wClassName<<" size: "<<wcTextSize<<endl; PRINTOUT;
    if(wClassName != NULL)
        copyWCharString(wClassName, wcTextSize, buffer);

    GetWindowText(wndHwnd, buffer, bufferSizeMax);
    wcTextSize        = wcslen(buffer)+SPACE_FOR_NULL;
    wTitleName        = new WCHAR[wcTextSize];
    //OUTSTREAM<<endl<<"tName: "<<wcToString(buffer)<<" ptr_tName: "<<wTitleName<<" size: "<<wcTextSize<<endl; PRINTOUT;
    if(wTitleName != NULL)
        copyWCharString(wTitleName, wcTextSize, buffer);
}

/******************************************************************************
*
*               create window class members
*
******************************************************************************/
window_create::window_create(window_info wnd, deploy dmode, visibl vmode, style smode)
{
    this->window_create::window_create();

    new_window  = wnd;
    deploy_mode = dmode;
    visibl_mode = vmode;
    style_mode  = smode;
}

FunctionSuccesFailure window_create::validateData() const
{ 
    if( new_window.wClassName == NULL || new_window.wTitleName == NULL || new_window.WndProc == NULL)
        return FUNCTION_FAILURE;
         
    return FUNCTION_SUCCESS;
}

FunctionSuccesFailure window_create::registerClass() const
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;
    WNDCLASSEX wc = {0};
    HINSTANCE hInstance = GetModuleHandle(NULL);

    if( GetClassInfoEx(hInstance, new_window.wClassName, &wc) != 0 ) {
        fresult = FUNCTION_SUCCESS;
    }
    else {    
        wc.cbSize        = sizeof(WNDCLASSEX);
        wc.style         = 0;
        wc.lpfnWndProc   = new_window.WndProc;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = hInstance;
        wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
        wc.hCursor       = LoadCursor(NULL, IDC_WAIT);
        wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wc.lpszMenuName  = NULL;
        wc.lpszClassName = new_window.wClassName;
        wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

        if(RegisterClassEx(&wc) != 0)
            fresult = FUNCTION_SUCCESS;
    }

    return fresult;
}

FunctionSuccesFailure window_create::unregisterClass() const
{
    HINSTANCE hInstance = GetModuleHandle(NULL);

    if(UnregisterClass(new_window.wClassName, hInstance) == 0)
             return FUNCTION_FAILURE;
    return FUNCTION_SUCCESS;
}

VOID window_create::messagePump() const
{
    BOOL inner_loop_failure = FALSE;
    BOOL outer_loop_failure = FALSE;
    BOOL bResult = FALSE;

    window_info wnd;
    do {
        MSG message = {0};
        while( bResult = GetMessage(&message, NULL, 0, 0) ) {

            if(bResult == -1)
                inner_loop_failure = FUNCTION_FAILURE;
            if(message.message == WM_CLOSE)
                wnd = window_info(message.hwnd);

            TranslateMessage( &message );
            DispatchMessage ( &message );
        }
    }while( (outer_loop_failure = isClassWndLast(wnd)) == FALSE);

    if( inner_loop_failure == FUNCTION_FAILURE || outer_loop_failure == FUNCTION_FAILURE   ||
        waitWndExpiration(wnd.hwnd) == FUNCTION_FAILURE                                     )
            stream::error_message("wnds_mgmt", "message pump killed, faults occured"); 
    else
        stream::system_message("wnds_mgmt", "message pump killed");
}

FunctionSuccesFailure window_create::create() const
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;

    if( validateData() == FUNCTION_SUCCESS && registerClass() == FUNCTION_SUCCESS ) {

        HINSTANCE hInstance = GetModuleHandle(NULL);
        stream::system_message("wnds_mgmt", "hinst: "+decToHexString((UINT_PTR)hInstance) );
        
        DWORD styleFlags = WS_VISIBLE;
        switch(style_mode) {
            case WND_STY_NORMAL:
                break;
            case WND_STY_MAXIMIZED:
                styleFlags |= WS_MAXIMIZE;
                break;
            case WND_STY_NORMAL_POPUP:
                styleFlags |= WS_POPUP;
                break;
            case WND_STY_MAXIMIZED_POPUP:
                styleFlags |= (WS_MAXIMIZE|WS_POPUP);
                break;
            default:
            { /* no action defined */ }
        }

        HWND hwnd = CreateWindowEx(
                                    WS_EX_LAYERED, new_window.wClassName, new_window.wTitleName, styleFlags,
                                    new_window.start_X, new_window.start_Y, new_window.width, new_window.height,
                                    0, NULL, hInstance, NULL
                                  );

        if(hwnd != 0) {
            stream::system_message("wnds_mgmt", "window created, hwnd: "+decToHexString((UINT_PTR)hwnd));
            SetWindowLong(hwnd,  GWL_WNDPROC, (LONG)new_window.WndProc);

            switch(visibl_mode) {
                case WND_VIS_TRANSPARENT:
                    SetLayeredWindowAttributes(hwnd, 0, 90, LWA_ALPHA);
                    break;
                case WND_VIS_OPAQUE:
                    SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);
                    break;
                default:
                { /* no action defined */ }
            }

            switch(deploy_mode) {
                case WND_DEP_DEPLOY:
                    messagePump();
                    unregisterClass();
                    stream::system_message("wnds_mgmt", "class unregistered");
                    break;
                case WND_DEP_NOTDEPLOY:
                    break;
                default:
                { /* no action defined */ }
            }

            fresult = FUNCTION_SUCCESS;
        }
    }

    return fresult;
}

/******************************************************************************
*
*               window exposure functions
*
******************************************************************************/
FunctionSuccesFailure bringConsoleForeground()
{
    HWND hConsole = GetConsoleWindow();

    if( hConsole != GetForegroundWindow() ) {

        SetForegroundWindow(hConsole);
        while( hConsole != GetForegroundWindow() ) {
            DWORD lockTimeOut    = 0;
            DWORD lockTimeOutNew = 0;
            DWORD dwThisTID      = GetCurrentThreadId();
            DWORD dwCurrTID      = GetWindowThreadProcessId(GetForegroundWindow(), NULL);

            AttachThreadInput(dwThisTID, dwCurrTID, TRUE);

            SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &lockTimeOut, 0);
            SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, &lockTimeOutNew, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);

            AllowSetForegroundWindow(ASFW_ANY);
            SetForegroundWindow(hConsole);

            SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, &lockTimeOut, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
            AttachThreadInput(dwThisTID, dwCurrTID, FALSE);
        }
    }

    if( hConsole == GetForegroundWindow() )
        return FUNCTION_SUCCESS;

    return FUNCTION_FAILURE;
}

FunctionSuccesFailure bringWindowForeground(const HWND hwnd)
{
    if( hwnd != GetForegroundWindow() ) {

        SetForegroundWindow(hwnd);
        while( hwnd != GetForegroundWindow() ) {
            DWORD lockTimeOut    = 0;
            DWORD lockTimeOutNew = 0;
            DWORD dwThisTID      = GetCurrentThreadId();
            DWORD dwCurrTID      = GetWindowThreadProcessId(GetForegroundWindow(), NULL);

            AttachThreadInput(dwThisTID, dwCurrTID, TRUE);

            SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &lockTimeOut, 0);
            SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, &lockTimeOutNew, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);

            AllowSetForegroundWindow(ASFW_ANY);
            SetForegroundWindow(hwnd);

            SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, &lockTimeOut, SPIF_SENDWININICHANGE | SPIF_UPDATEINIFILE);
            AttachThreadInput(dwThisTID, dwCurrTID, FALSE);
        }
    }

    if( hwnd == GetForegroundWindow() )
        return FUNCTION_SUCCESS;

    return FUNCTION_FAILURE;
}

FunctionSuccesFailure getMonitorDimentions(UINT &width, UINT &height)
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;
    HWND hWnd = GetShellWindow();
    width  = 0;
    height = 0;

    if(hWnd != 0) {
        HMONITOR    monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY);
        MONITORINFO minfo   = {0}; 
        minfo.cbSize        = sizeof(MONITORINFO);

        if(GetMonitorInfo(monitor, &minfo) != 0) {
            if(minfo.rcMonitor.right > minfo.rcMonitor.left)
                width  = minfo.rcMonitor.right - minfo.rcMonitor.left;
            else
                width  = minfo.rcMonitor.left - minfo.rcMonitor.right;

            if(minfo.rcMonitor.bottom > minfo.rcMonitor.top) 
                height = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
            else
                height = minfo.rcMonitor.top - minfo.rcMonitor.bottom;

            fresult = FUNCTION_SUCCESS;
        }
    }

    return fresult;
}

FunctionSuccesFailure getWindowDimentions(UINT_PTR hWndRaw, UINT &width, UINT &height)
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;
    width  = 0;
    height = 0;

    if(hWndRaw != 0) {
        HWND hWnd           = (HWND)hWndRaw;
        HMONITOR    monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY);
        MONITORINFO minfo   = {0}; 
        minfo.cbSize        = sizeof(MONITORINFO);

        if(GetMonitorInfo(monitor, &minfo) != 0) {
            if(minfo.rcMonitor.right > minfo.rcMonitor.left)
                width  = minfo.rcMonitor.right - minfo.rcMonitor.left;
            else
                width  = minfo.rcMonitor.left - minfo.rcMonitor.right;

            if(minfo.rcMonitor.bottom > minfo.rcMonitor.top) 
                height = minfo.rcMonitor.bottom - minfo.rcMonitor.top;
            else
                height = minfo.rcMonitor.top - minfo.rcMonitor.bottom;

            fresult = FUNCTION_SUCCESS;
        }
    }

    return fresult;
}

/******************************************************************************
*
*               windows searching functions
*
******************************************************************************/
VOID window_search::startSearch() const
{
    EnumWindows( EnumWndProc, (LPARAM)(this) );
}

window_search::search_mode window_search::get_searchMode() const
{
    search_mode curr_mode = WND_SRCH_UNDEF;

    if( wndData.wClassName != NULL && wndData.wTitleName != NULL )
        curr_mode = WND_SRCH_TITLE_N_CLASS;
    else if( wndData.wClassName == NULL && wndData.wTitleName != NULL )
        curr_mode = WND_SRC_TITLE;
    else if( wndData.wClassName != NULL && wndData.wTitleName == NULL )
        curr_mode = WND_SRCH_CLASS;
    else
        curr_mode = WND_SRCH_ALL;

    return curr_mode;
}

BOOL window_search::is_matched(window_info wnd) const
{
    BOOL match_result = FALSE;

    switch(get_searchMode()) {
        case WND_SRCH_ALL:
            match_result = TRUE;
            break;
        case WND_SRC_TITLE:
            if( is_titleSame(wnd.wTitleName) == TRUE )
                match_result = TRUE;
            break;
        case WND_SRCH_CLASS:
            if( is_cNameSame(wnd.wClassName) == TRUE )
                match_result = TRUE;
            break;
        case WND_SRCH_TITLE_N_CLASS:
            if( is_cNameSame(wnd.wClassName) == TRUE && is_titleSame(wnd.wTitleName) == TRUE )
                match_result = TRUE;
            break;
        default: { /* no action determined */ }
    }

    return match_result;
}

window_info window_search::getExtracted()
{
    if(isExtractedEmpty() == TRUE)
        return window_info();

    const window_info first = extracted.front();
    extracted.erase(extracted.begin());

    return first;
}

/******************************************************************************
*
*               windows closing functions
*
******************************************************************************/
FunctionSuccesFailure closeWindows(window_info &wnd)
{
    UINT_PTR closeWndThread = processes::instance().thread_create(closeWindowsProc, (LPVOID)&wnd, 0);

    if(closeWndThread == NULL)
        return FUNCTION_FAILURE;
    return FUNCTION_SUCCESS;
}

DWORD WINAPI closeWindowsProc(LPVOID lpParam)
{
    window_info wnd = *(window_info*)(lpParam);
    processes::thread_ready();

    window_search search(wnd);
    UINT closing_failures = 0;

    if(search.isExtractedEmpty() == FALSE) {
        while(search.isExtractedEmpty() == FALSE)
            if(closeOneWindow(search.getExtracted()) == FUNCTION_FAILURE)
                closing_failures++;
    }
    else {
        static const string allWndsName = "all[*]";
        string wTitle = wcToString(wnd.wTitleName);
        string wClass = wcToString(wnd.wClassName);

        if(wTitle == UNNAMED)
            wTitle = allWndsName;
        if(wClass == UNNAMED)
            wClass = allWndsName;

        stream::system_message("wnds_mgmt", "Window of class:'"+wClass+"' and name:'"+wTitle+"' does not exist or already closed");
    }

    if(closing_failures == 0)
        return EXIT_SUCCESS;
    return EXIT_FAILURE;
}

FunctionSuccesFailure closeOneWindow(window_info &wndToClose)
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;

    PostMessage(wndToClose.hwnd, WM_CLOSE, 0, 0);
    if(waitWndExpiration(wndToClose.hwnd) == FUNCTION_SUCCESS) {
        stream::system_message("wnds_mgmt", "Window[hwnd]: "+wcToString(wndToClose.wTitleName)
                               +" | "+wcToString(wndToClose.wClassName)
                               +" ["+decToHexString((DWORD)wndToClose.hwnd)+"] closed.");
        fresult = FUNCTION_SUCCESS;
    }
    else {
        stream::error_message("wnds_mgmt", "CANNOT close window[hwnd]: "+wcToString(wndToClose.wTitleName)
                                +" | "+wcToString(wndToClose.wClassName)
                                +" ["+decToHexString((UINT)wndToClose.hwnd)+"]");
    }

    return fresult;
}

/******************************************************************************
*
*               windows text functions
*
******************************************************************************/
UINT_PTR const window_text::wndTextEvent = processes::event_create(FALSE, UNNAMED);

FunctionSuccesFailure fontDefaultSettings(LOGFONT &fontDefaultStruct)
{
    fontDefaultStruct.lfHeight      = 50;
    fontDefaultStruct.lfWidth       = 0;
    fontDefaultStruct.lfEscapement  = 0;
    fontDefaultStruct.lfOrientation = 0;
    fontDefaultStruct.lfWeight      = FW_NORMAL;
    fontDefaultStruct.lfItalic      = FALSE;
    fontDefaultStruct.lfUnderline   = FALSE;
    fontDefaultStruct.lfStrikeOut   = FALSE;
    fontDefaultStruct.lfQuality     = ANTIALIASED_QUALITY;
    fontDefaultStruct.lfCharSet        = DEFAULT_CHARSET;
    fontDefaultStruct.lfOutPrecision   = OUT_DEFAULT_PRECIS;
    fontDefaultStruct.lfClipPrecision  = CLIP_DEFAULT_PRECIS;
    fontDefaultStruct.lfPitchAndFamily = DEFAULT_PITCH;

    if(copyWCharString(fontDefaultStruct.lfFaceName, LF_FACESIZE-1, L"Consolas") == FUNCTION_SUCCESS)
        return FUNCTION_SUCCESS;

    return FUNCTION_FAILURE;
}

INT fontFactorInit(UINT_PTR hWindowRaw)
{
    static const UINT fontDivFactor    = 72;
    static const INT  fontSignedFactor = -1;

    if(hWindowRaw == NULL)
        hWindowRaw = (UINT_PTR)GetForegroundWindow();

    HWND hWindow = (HWND)hWindowRaw;
    HDC  hdc     = GetDC(hWindow);
    UINT fontFactor = 0;

    SetMapMode(hdc, MM_TEXT);
    fontFactor = GetDeviceCaps(hdc, LOGPIXELSY);
    ReleaseDC(hWindow, hdc);

    return fontSignedFactor*(fontFactor/fontDivFactor);
}

FunctionSuccesFailure window_text::invalidate()
{
    static const UINT invalidateMsTimeout    = 25;
    static const UINT invalidateMsAdditional = 5;
    HWND hWindow = (HWND)hWindowRaw;

    InvalidateRect(hWindow, NULL, TRUE);
    processes::event_reset(wndTextEvent);
    if(processes::event_wait(wndTextEvent, invalidateMsTimeout) == FUNCTION_SUCCESS) {
        timers::timeout_create(invalidateMsAdditional);
        return FUNCTION_SUCCESS;
    }

    return FUNCTION_FAILURE;
}

UINT_PTR window_text::font_adjust(UINT size, BOOL bBold, BOOL bItalic, BOOL bUnderline)
{
    HFONT hFontNew       = NULL;
    UINT_PTR hFontNewRaw = NULL;
    LOGFONT  defaultFont  = {0};

    fontDefaultSettings(defaultFont);
    defaultFont.lfItalic = bItalic;
    defaultFont.lfUnderline = bUnderline;

    if(size != 0)
        defaultFont.lfHeight = getFontConverteredSize(size);
    if(bBold == TRUE)
        defaultFont.lfWeight = FW_EXTRABOLD;

    hFontNew = CreateFont(defaultFont.lfHeight, defaultFont.lfWidth,
                    defaultFont.lfEscapement, defaultFont.lfOrientation,
                    defaultFont.lfWeight, defaultFont.lfItalic,
                    defaultFont.lfUnderline, defaultFont.lfStrikeOut,
                    defaultFont.lfCharSet, defaultFont.lfOutPrecision,
                    defaultFont.lfClipPrecision, defaultFont.lfQuality,
                    defaultFont.lfPitchAndFamily, defaultFont.lfFaceName);

    hFontNewRaw = (UINT_PTR)hFontNew;
    return hFontNewRaw;
}

FunctionSuccesFailure
window_text::printText(UINT posX, UINT posY, UINT text_size, BOOL bBold, BOOL bItalic, BOOL bUnderline, const string &text)
{
    FunctionSuccesFailure fresult = FUNCTION_FAILURE;
    SIZE textDimentions  = {0};
    HFONT hFontNew   = NULL;
    HWND  hWindow    = (HWND)hWindowRaw;
    wstring wtext    = stringToWc(text);
    UINT textPosX    = posX;
    UINT textPosY    = posY;
    UINT widthLimit  = 0;
    UINT heightLimit = 0;

    hFontNew = (HFONT)font_adjust(text_size, bBold, bItalic, bUnderline);

    HDC hdc = GetDC(hWindow);
    if( (hdc != NULL)                            &&
        (SelectObject(hdc, hFontNew) != NULL)    &&
        (SetTextColor(hdc, 0x0) != CLR_INVALID)   )
    {
        GetTextExtentPoint32(hdc, wtext.c_str(), wtext.size(), &textDimentions);
        getWindowDimentions((UINT_PTR)hWindow, widthLimit, heightLimit);
        textPosX %= widthLimit  - textDimentions.cx + 1;
        textPosY %= heightLimit - textDimentions.cy + 1;

        if( (invalidate() != FUNCTION_FAILURE)                                        &&
            (TextOut(hdc, textPosX, textPosY, wtext.c_str(), wtext.size()))  != NULL   ) {
            fresult = FUNCTION_SUCCESS;
        }

        if(ReleaseDC(hWindow, hdc) == NULL) {
            stream::error_message("wnds_mgmt", "DC device context not released");
            return FUNCTION_FAILURE;
        }
    }

    return fresult;
}

FunctionSuccesFailure
window_text::printTextRandomPosition(UINT text_size, BOOL bBold, BOOL bItalic, BOOL bUnderline, const string &text)
{
    static FunctionSuccesFailure randomGeneratorInit = randomInit();
    return printText(rand(), rand(), text_size, bBold, bItalic, bUnderline, text);
}

FunctionSuccesFailure window_text::randomInit()
{
    srand((UINT)time(NULL));
    return FUNCTION_SUCCESS;
}

INT window_text::getFontConverteredSize(UINT size)
{
    static UINT frontScaleFactor = 0;
    static const UINT stepSize   = 3;
    if(frontScaleFactor == 0)
        frontScaleFactor = fontFactorInit(hWindowRaw);

    return stepSize*size*frontScaleFactor;
}

/******************************************************************************
*
*               window auxiliary functions
*
******************************************************************************/
INT isClassWndLast(const window_info &wnd)
{
    INT fresult = FUNCTION_FAILURE;

    if(*wnd.wClassName != NULL) {
        BOOL isWndExists = IsWindow(wnd.hwnd);
        window_search is_last(window_info(wcToString(wnd.wClassName)));

        if( (isWndExists == TRUE  && is_last.isExtractedLast()  == TRUE)  ||
            (isWndExists == FALSE && is_last.isExtractedEmpty() == TRUE)   )
                fresult = TRUE;
        else
            fresult = FALSE;
    }

    return fresult;
}

FunctionSuccesFailure waitWndExpiration(HWND hwnd)
{
    if( IsWindow(hwnd) == FALSE                                                    ||
       (timers::timeout_create(20) == FUNCTION_SUCCESS && IsWindow(hwnd) == FALSE)  )
        return FUNCTION_SUCCESS;

    return FUNCTION_FAILURE;
}

BOOL &getWndProcUpdateBlockade()
{
    static BOOL WndProcUpdateBlockade = FALSE;
    return WndProcUpdateBlockade;
}

BOOL isWndProcUpdateBlockadeActive()
{
    return getWndProcUpdateBlockade();
}

VOID setWndProcUpdateBlockade(BOOL blockadeState)
{
    BOOL &currentBlockadeStatus = getWndProcUpdateBlockade();
    currentBlockadeStatus = blockadeState;
}

